// ------------------------------------------------------------------------------------------------
// File   : DX9Shader.h
// Date   : 2012-08-01
// Author : Weng xiao yi
// Descr. : 
//
// ------------------------------------------------------------------------------------------------

#ifndef OY_DX9SHADER_H
#define OY_DX9SHADER_H


#include "DX9ShaderUtils.h"
#include "DX9ShaderDatabase.h"

#define SHADER_DEBUG			1

// DX9Shader class definition -------------------------------------

template <class ShaderInterface, ULONG ulConstCount>
class DX9Shader
{
public:

	DX9Shader();
	virtual ~DX9Shader();

	// utility
	void SetGlobalVector(ULONG _eConst, const OyVector4D *_pVector);
	void SetGlobalVectorArray(ULONG _eConst, OyVector4D *_pVector, INT _iCount);
	void SetGlobalMatrix(ULONG _eConst, const OyMatrix *_pMatrix);
	void SetGlobalMatrixArray(ULONG _eConst, OyMatrix *_pMatrixArray, INT _iCount);
	void SetGlobalFloat(ULONG _eConst, FLOAT _fValue);
	void SetGlobalFloatArray(ULONG _eConst, FLOAT *_pfValue, INT _iCount);
	void SetGlobalInt(ULONG _eConst, INT _iValue);
	void SetGlobalIntArray(ULONG _eConst, INT *_piValue, INT _iCount);
	void SetGlobalVectorStruct(ULONG _eConst, OyVector4D *_pVector);
	void SetGlobalRawData(ULONG _eConst, OyVector4D *_pVector, ULONG ulVectorCount);
	void SetGlobalMatrixAsFloat4x3(ULONG _eConst, const OyMatrix *_pMatrix);
	void SetGlobalMatrixAsFloat4x2(ULONG _eConst, const OyMatrix *_pMatrix);
	void SetGlobalFloat4x3(ULONG _eConst, const Float4x3 *_pMatrix);
	void SetGlobalFloat4x2(ULONG _eConst, const Float4x2 *_pMatrix);

	inline ShaderInterface GetShaderInterface() { return m_pShaderInterface; }
	inline LPDIRECT3DDEVICE9 GetD3DDevice() { return DX9GetShaderDatabase()->m_pDevice; }
	BOOL IsGlobalConstPresent(ULONG _eConst);
	inline void SetAutoGenerated(BOOL _bAutoGenerated) { m_bAutoGenerated = _bAutoGenerated; }
	inline BOOL IsAutoGenerated(void) { return m_bAutoGenerated; }
	void SetConstantTable(ID3DXConstantTable* _pTable);
	UINT GetRegisterCount(ULONG _eConst);

	BOOL IsDefault( ) { return m_bIsDefault; }
	void IsDefault( BOOL _bIsDefault ) { m_bIsDefault = _bIsDefault; }

	ShaderInterface          m_pShaderInterface;
	LPD3DXCONSTANTTABLE      m_pConstantTable;
	D3DXHANDLE               m_ahConstant[ulConstCount];
	LONG                     m_lRegister[ulConstCount];

	BOOL                     m_bAutoGenerated;
	BOOL                     m_bIsDefault;

#ifdef SHADER_DEBUG
	inline void              SetFilename(char *_szFilename);
	char                     m_Filename[MAX_PATH];      // shader file name for debugging purposes
#endif
};

template <class ShaderInterface, ULONG ulConstCount>
DX9Shader<ShaderInterface, ulConstCount>::DX9Shader()
{
	m_pShaderInterface  = NULL;
	m_pConstantTable    = NULL;
	m_bAutoGenerated    = FALSE;
	m_bIsDefault        = FALSE;

#ifdef SHADER_DEBUG
	m_Filename[0] = 0;
#endif
}

template <class ShaderInterface, ULONG ulConstCount>
DX9Shader<ShaderInterface, ulConstCount>::~DX9Shader()
{
	DX9_SAFE_RELEASE(m_pShaderInterface);
	DX9_SAFE_RELEASE(m_pConstantTable);
}


template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalVector(ULONG _eConst, const OyVector4D *_pVector)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetVector(GetD3DDevice(), m_ahConstant[_eConst], (D3DXVECTOR4*) _pVector);
}

template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalVectorArray(ULONG _eConst, OyVector4D *_pVector, INT _iCount)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetVectorArray(GetD3DDevice(), m_ahConstant[_eConst], (D3DXVECTOR4*) _pVector, _iCount);
}

template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalMatrix(ULONG _eConst, const OyMatrix *_pMatrix)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetMatrix(GetD3DDevice(), m_ahConstant[_eConst], _pMatrix);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalMatrixArray(ULONG _eConst, OyMatrix *_pMatrixArray, INT _iCount)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetMatrixArray(GetD3DDevice(), m_ahConstant[_eConst], _pMatrixArray, _iCount);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalFloat(ULONG _eConst, FLOAT _fValue)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetFloat(GetD3DDevice(), m_ahConstant[_eConst], _fValue);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalFloatArray(ULONG _eConst, FLOAT *_pfValue, INT _iCount)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetFloatArray(GetD3DDevice(), m_ahConstant[_eConst], _pfValue, _iCount);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalInt(ULONG _eConst, INT _iValue)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetInt(GetD3DDevice(), m_ahConstant[_eConst], _iValue);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalIntArray(ULONG _eConst, INT *_piValue, INT _iCount)
{
	DX9Verify(_eConst < ulConstCount);
	m_pConstantTable->SetIntArray(GetD3DDevice(), m_ahConstant[_eConst], _piValue, _iCount);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalVectorStruct(ULONG _eConst, OyVector4D *_pVector)
{
	DX9Verify(_eConst < ulConstCount);

	D3DXCONSTANT_DESC   oArrayDesc, oElementDesc;
	UINT                uiCount = 1;

	D3DXHANDLE hMemberHandle;
	D3DXHANDLE hElementHandle;

	// TODO Optimize : Members handle should be cached somewhere

	m_pConstantTable->GetConstantDesc(m_ahConstant[_eConst], &oArrayDesc, &uiCount);

	// loop for all elements except the last one...
	UINT uiCurElement = 0;
	for (; uiCurElement < (oArrayDesc.Elements-1); uiCurElement++)
	{
		hElementHandle = m_pConstantTable->GetConstantElement(m_ahConstant[_eConst], uiCurElement);
		m_pConstantTable->GetConstantDesc(hElementHandle, &oElementDesc, &uiCount);

		// loop for all members
		for (UINT uiCurMember = 0; uiCurMember < oElementDesc.StructMembers; uiCurMember++)
		{
			hMemberHandle = m_pConstantTable->GetConstant(hElementHandle, uiCurMember);
			m_pConstantTable->SetVector(GetD3DDevice(), hMemberHandle, (D3DXVECTOR4*) &_pVector[uiCurElement*oElementDesc.StructMembers + uiCurMember]);
		}
	}

	// Handle the last one !
	if (oArrayDesc.Elements > 0)
	{
		hElementHandle = m_pConstantTable->GetConstantElement(m_ahConstant[_eConst], uiCurElement);
		m_pConstantTable->GetConstantDesc(hElementHandle, &oElementDesc, &uiCount);

		// loop for all members
		for (UINT uiCurMember = 0; uiCurMember < oElementDesc.RegisterCount; uiCurMember++)
		{
			hMemberHandle = m_pConstantTable->GetConstant(hElementHandle, uiCurMember);
			m_pConstantTable->SetVector(GetD3DDevice(), hMemberHandle, (D3DXVECTOR4*) &_pVector[uiCurElement*oElementDesc.StructMembers + uiCurMember]);
		}
	}

}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalRawData(ULONG _eConst, OyVector4D *_pVector, ULONG ulVectorCount)
{
	HRESULT hr;
	DX9Verify(_eConst < ulConstCount && m_lRegister[_eConst] != -1);

#ifdef SHADER_DEBUG
	UINT uiRegisterCount = GetRegisterCount(_eConst);
	DX9Verify( uiRegisterCount >= ulVectorCount );
#endif

	hr = GetD3DDevice()->SetVertexShaderConstantF( m_lRegister[_eConst], (const float*)_pVector, ulVectorCount);
	DX9Verify(SUCCEEDED(hr)); 
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
inline BOOL DX9Shader<ShaderInterface, ulConstCount>::IsGlobalConstPresent(ULONG _eConst)
{
	return (m_ahConstant[_eConst] != 0);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetConstantTable(ID3DXConstantTable* _pTable)
{
	DX9Verify(_pTable != NULL);

	m_pConstantTable = _pTable;
	m_pConstantTable->AddRef();
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------

template <class ShaderInterface, ULONG ulConstCount>
inline
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalMatrixAsFloat4x3(ULONG _eConst, const OyMatrix *_pMatrix)
{
	DX9Verify(_pMatrix);

	Float4x3 oMatrix;
	DX9ConvertToFloat4x3( oMatrix, *_pMatrix);
	SetGlobalRawData( _eConst, (OyVector4D*)&oMatrix, 3 );

}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------

template <class ShaderInterface, ULONG ulConstCount>
inline
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalMatrixAsFloat4x2(ULONG _eConst, const OyMatrix *_pMatrix)
{
	DX9Verify(_pMatrix);

	Float4x2 oMatrix;
	DX9ConvertToFloat4x2( oMatrix, *_pMatrix);
	SetGlobalRawData( _eConst, (OyVector4D*)&oMatrix, 2 );

}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
inline
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalFloat4x3(ULONG _eConst, const Float4x3 *_pFloat4x3 )
{
	DX9Verify(_pFloat4x3);
	SetGlobalRawData( _eConst, (OyVector4D*)_pFloat4x3, 3);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
template <class ShaderInterface, ULONG ulConstCount>
inline
void DX9Shader<ShaderInterface, ulConstCount>::SetGlobalFloat4x2(ULONG _eConst, const Float4x2 *_pFloat4x2 )
{
	DX9Verify(_pFloat4x2);
	SetGlobalRawData( _eConst, (OyVector4D*)_pFloat4x2, 2);
}

// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------

template <class ShaderInterface, ULONG ulConstCount>
inline
UINT DX9Shader<ShaderInterface, ulConstCount>::GetRegisterCount(ULONG _eConst)
{
	D3DXCONSTANT_DESC oDesc;
	UINT uiCount=1;
	HRESULT hr = m_pConstantTable->GetConstantDesc(m_ahConstant[_eConst], &oDesc, &uiCount);
	DX9Verify(SUCCEEDED(hr));
	return oDesc.RegisterCount;
}


// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
#ifdef SHADER_DEBUG
template <class ShaderInterface, ULONG ulConstCount>
void DX9Shader<ShaderInterface, ulConstCount>::SetFilename(char *_szFilename)
{
	if(_szFilename != NULL)
	{
		strcpy_s(m_Filename, _szFilename);
	}
}
#endif

#endif